package com.lqwu.mallchatserver.user.service.impl;

import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.lqwu.mallchatserver.common.domain.vo.response.ApiResult;
import com.lqwu.mallchatserver.common.utils.JsonUtils;
import com.lqwu.mallchatserver.user.model.entry.IpDetail;
import com.lqwu.mallchatserver.user.model.entry.IpInfo;
import com.lqwu.mallchatserver.user.model.entry.User;
import com.lqwu.mallchatserver.user.service.IpService;
import com.lqwu.mallchatserver.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * ip 相关服务实现
 */
@Service
@Slf4j
public class IpServiceImpl implements IpService , DisposableBean {
    /**
     * ip 解析的异步线程池
     */
    private static ExecutorService executor = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(500), new NamedThreadFactory("refresh-ipDetail", false));

    @Resource
    private UserService userService;

    /**
     * 异步解析ip信息
     * @param id
     */
    @Override
    public void refreshIpDetailAsync(Long id) {
        // 线程池中执行
        executor.execute(() -> {
            // 1.获取用户ip信息
            User user = userService.getById(id);
            IpInfo ipInfo = user.getIpInfo();
            if (ipInfo == null) return;
            // 2.判断是否需要更新
            String ip = ipInfo.needRefreshIp();
            if (StrUtil.isBlank(ip)) return;
            // 3.ip 刷新
            // 获取新的 ip 详情
            IpDetail ipDetail = tryGetIpDetailOrNullThreeTimes(ip);
            // 4.拿到ip详情。更新
            if (ipDetail != null){
                ipInfo.refreshIpDetail(ipDetail);
                // 5.更新用户ip信息
                User updateUser = new User();
                updateUser.setId(id);
                updateUser.setIpInfo(ipInfo);
                userService.updateById(updateUser);
            }
        });
    }

    /**
     * 尝试三次获取ip详情
     * @param ip
     * @return
     */
    private IpDetail tryGetIpDetailOrNullThreeTimes(String ip) {
        // 尝试获取ip详情，最多尝试3次
        for (int i = 0; i < 3; i++) {
            IpDetail ipDetail = getIpDetailOrNull(ip);
            if (ipDetail != null){
                // 获取成功,返回
                return ipDetail;
            }
            try {
                // 获取失败，等待2秒
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                log.error("第{}次获取ip详情失败", i, e);
            }
        }
        // 三次获取失败，返回null
        return null;
    }

    /**
     * 根据ip获取ip详情
     * @param ip
     * @return
     */
    private IpDetail getIpDetailOrNull(String ip) {
        try {
            // 1.发送请求获取ip详情
            String body = HttpUtil.get("https://ip.taobao.com/outGetIpInfo?ip=" + ip + "&accessKey=alibaba-inc");
            if (StrUtil.isBlank(body)) {
                // 请求失败，返回null
                return null;
            }
            // 2.解析ip详情
            ApiResult<IpDetail> ipDetailApiResult = JsonUtils.toObj(body, new TypeReference<ApiResult<IpDetail>>() {});
            return ipDetailApiResult.getData();
        }catch (Exception e){
            return null;
        }
    }

    /**
     * 优雅停机
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        executor.shutdown();
        if (!executor.awaitTermination(30000, TimeUnit.MILLISECONDS)){//  最多等待30秒，否则强制关闭
            if (log.isErrorEnabled()){
                log.error("ip 解析线程池关闭失败");
            }
        }
    }
}
